/**
 * @file rf24.c
 * @brief Implementation of core RF24 library functions
 * @author Jeff Eaton
 */

#include "rf24.h"

// static variables and functions internal to the RF24 library
static volatile uint16_t *LAT_REG_CE;
static uint8_t CE_pin;
static volatile uint16_t *PORT_REG_IRQ;
static uint8_t IRQ_pin;

static uint64_t pipe0_read_address = 0;
static const uint8_t rx_pipes[] = {RX_ADDR_P0, RX_ADDR_P1, RX_ADDR_P2, RX_ADDR_P3, RX_ADDR_P4, RX_ADDR_P5};
static const uint8_t rx_pipe_payloads[] = {RX_PW_P0, RX_PW_P1, RX_PW_P2, RX_PW_P3, RX_PW_P4, RX_PW_P5};
static uint8_t ack_width;
static bool dynamicPayloadsEnabled = false;
static bool noACKEnabled = false;
static uint8_t payloadSize = 32;
static uint8_t payloadSizes[] = {32, 32, 32, 32, 32, 32}; // payload sizes for each rx pipe

static void clearSPI();
static void rf_CE(const bool enable);
static void rf_powerUp();
static void rf_powerDown();
static uint8_t rf_readRegister(const uint8_t regMapAddr, uint8_t* buf, uint8_t len);
static uint8_t rf_writeRegister(const uint8_t regMapAddr, const uint8_t* buf, uint8_t len);
static uint8_t rf_writeRegisterSimple(const uint8_t regMapAddr, const uint8_t value);
static uint8_t rf_readPayload(uint8_t* buf, uint8_t len);
static uint8_t rf_writePayload(const uint8_t* buf, uint8_t len);


void rf_init(volatile uint16_t* m_LAT_REG_CE, const uint8_t m_CE_pin,
        volatile uint16_t* m_PORT_REG_IRQ, const uint8_t m_IRQ_pin) {
    LAT_REG_CE = m_LAT_REG_CE;
    CE_pin = m_CE_pin;
    PORT_REG_IRQ = m_PORT_REG_IRQ;
    IRQ_pin = m_IRQ_pin;
    
    rf_CE(false);

    // configure timer 1
    T1CON = 0; // (use FCY for clock, 1:1 prescale, not gated, timer off)
    IEC0bits.T1IE = 0; // we don't use an interrupt because we want blocking
    IFS0bits.T1IF = 0; // but we do use the interrupt flag as a "timer done" bit

    // put some delay so the RF module can wake up (5ms)
    TMR1 = 0;
    PR1 = 782;
    T1CONbits.TCKPS = 0b11; // 256:1 prescale
    T1CONbits.TON = 1;
    while(!IFS0bits.T1IF);
    T1CONbits.TON = 0;
    T1CONbits.TCKPS = 0b00; // 1:1 prescale

    // enable IRQ interrupt masking and 2 byte crc
    rf_writeRegisterSimple(CONFIG, 0x0C);

    // 1000us auto transmit delay, 15 retry attempts
    rf_writeRegisterSimple(SETUP_RETR, 0x3F);

    // set maximum power level (0dBm)
    rf_setPALevel(RF24_0_DBM);
    
    // turn off carrier testing bits if they were on
    rf_sendCarrier(false);

    // set reasonable speed (1Mbps)
    rf_setDataRate(RF24_1MBPS);

    // enable auto acknowledgements (also enhanced shockburst)
    rf_writeRegisterSimple(EN_AA, 0x3F);

    // set static payload length for all RX pipes
    // untested!
    //rf_setStaticPayloadWidth(0xFF, 32);

    // enable dynamic payload length
    rf_enableDynamicPayloads(false);

    // disable No ACK mode
    rf_setNoACK(false);
        
    // switch to two byte (16 bit) CRC checking
    rf_setCRCMode(RF24_CRC_TWO_BYTES);

    // 5 byte addresses
    rf_setAddressWidth(RF24_AW_5BYTES);

    // reset current status
    rf_writeRegisterSimple(STATUS, 0x70);

    // pick a channel
    rf_setChannel(76);

    // flush buffers
    rf_flushRX();
    rf_flushTX();

    // power UP
    rf_powerUp();
    // WE NEVER POWER DOWN !
    // Y O L O
    
    // should be good to go at this point
}


uint8_t rf_flushTX() {
    uint8_t status;

    clearSPI();

    status = spi_write8(FLUSH_TX);

    return status;
}


uint8_t rf_flushRX() {
    uint8_t status;

    clearSPI();

    status = spi_write8(FLUSH_RX);

    return status;
}


void rf_openWritePipe(const uint64_t address) {
    uint8_t* tmp = (uint8_t*)(&address);

    rf_writeRegister(TX_ADDR, tmp, 5);
    rf_writeRegister(RX_ADDR_P0, tmp, 5);
}


void rf_openReadPipe(const uint8_t num, const uint64_t address) {
    if(num == 0) {
        pipe0_read_address = address;
    }

    // ports 6+ don't exist
    if(num >= 0 && num <= 5) {
        if(num < 2) {
            // the SETUP_AW register defaults to a 5 byte address for pipes 0 and 1
            rf_writeRegister(rx_pipes[num], (uint8_t*)(&address), 5);
        }
        else {
            // only write the LSB for the other pipes
            rf_writeRegister(rx_pipes[num], (uint8_t*)(&address), 1);
        }

        // write payload size
        if(!dynamicPayloadsEnabled) {
            rf_writeRegisterSimple(rx_pipe_payloads[num], payloadSize);
        }

        // enable RX pipes
        uint8_t en_rxaddr;
        rf_readRegister(EN_RXADDR, &en_rxaddr, 1);
        en_rxaddr |= (1 << num);
        rf_writeRegisterSimple(EN_RXADDR, en_rxaddr);
    }
}


void rf_startListening() {
    uint8_t config;

    rf_readRegister(CONFIG, &config, 1);
    // NOTE - this powers up the chip and we don't need to call rf_powerUp()
    config |= 0x03; // set the PWR_UP and PRIM_RX bits

    rf_writeRegister(CONFIG, &config, 1);   // write updated config register
    rf_writeRegisterSimple(STATUS, 0x70);      // reset status register

    // restore pipe 0 address if it was used for writing since last listening.
    // this is included because otherwise you'd have to manually reset the
    // pipe 0 rx address every time you switched from write to read mode
    if(pipe0_read_address) {
        rf_writeRegister(RX_ADDR_P0, (uint8_t*)(&pipe0_read_address), 5);
    }

    // flush buffers
    rf_flushRX();
    rf_flushTX();

    // chip enable line high
    rf_CE(true);

    // 130 us delay
    asm("Repeat #5200;"); Nop(); // 5200 * 1/40MHz = 130us
}


void rf_stopListening() {
    rf_CE(false);
    rf_flushTX();
    rf_flushRX();
}


bool rf_available(uint8_t* pipeNum) {
    uint8_t status = rf_readStatus();

    bool result = (status & 0x40); // RX_DR is bit 6
    *pipeNum = (status >> 1) & 0x07; // pipe number is bits 1-3

    //uint8_t fifo_status = rf_readRegister(FIFO_STATUS, &fifo_status, 1);
    //bool result = !(fifo_status & 0x01); // RX_DR is bit 6
    //*pipeNum = 0;

    return result;
}


bool rf_read(uint8_t* buf, uint8_t len) {
    uint8_t fifoStatus;

    rf_readPayload(buf, len);

    rf_readRegister(FIFO_STATUS, &fifoStatus, 1);

    // originally, this was in rf_available(), but moving it here fixed
    // a lockup that occurred at higher data rates
    rf_writeRegisterSimple(STATUS, 0x60); // clear RX_DR and TX_DS bits

    // returns 1 if the RX queue is empty
    return (fifoStatus & 0x01);    // RX_EMPTY is bit 0
}


void rf_write(const uint8_t* buf, uint8_t len) {
    bool result;
    uint8_t status;
    uint8_t config;
    rf_readRegister(CONFIG, &config, 1);

    // power up the transmitter and switch into TX mode
    // bit 0 is RX/TX control and bit 1 is PWR_UP state
    //rf_writeRegisterSimple(CONFIG, (config | 0x02) & ~0x01);
    rf_writeRegisterSimple(CONFIG, config & ~0x01);

    rf_writePayload(buf, len);

    // we're going for max speed, so just leave CE high and stay in either TX
    // or standby-2 mode
    rf_CE(true);

    // if we were going for minimum power consumption, we'd pulse CE for >10us
    // and also delay for 130us to let the tx mode oscillator lock

    // 15 us delay
    //asm("Repeat #600;"); Nop(); // 450 * 1/40MHz = 10us
    //rf_CE(false);

    // 130 us delay
    //asm("Repeat #5200;"); Nop(); // 5200 * 1/40MHz = 130us


    // wait for an ACK, max retries, or 0.4 seconds
    if(!noACKEnabled) {
        PR1 = 62500;
        TMR1 = 0;
        T1CONbits.TCKPS = 0b11; // 256:1
        IFS0bits.T1IF = 0;
        T1CONbits.TON = 1;

        uint8_t obs_tx;
        do {
            status = rf_readRegister(OBSERVE_TX, &obs_tx, 1);
        }while(!(status & 0x30) && !IFS0bits.T1IF);
        T1CONbits.TON = 0;
        T1CONbits.TCKPS = 0b00; // 1:1

        if(status & 0x20) { // if the tx_ds bit is set, transmission succeeded
            result = true;
        }
        if(status & 0x40) { // if the rx_dr bit is set, there's an ACK waiting
            rf_readRXPayloadWidth(&ack_width);
        }
    }
    rf_writeRegisterSimple(STATUS, 0x70); // reset status

    //rf_powerDown();

    //rf_flushTX();
    //rf_flushRX();
}


uint8_t rf_reuseTXPayload() {
    uint8_t status;

    clearSPI();

    status = spi_write8(REUSE_TX_PL);

    return status;
}


void rf_enableDynamicPayloads(const bool enable) {
    uint8_t feature;

    rf_readRegister(FEATURE, &feature, 1);

    dynamicPayloadsEnabled = enable;
    if(enable) {
        rf_writeRegisterSimple(FEATURE, feature | 0x04);
        rf_writeRegisterSimple(DYNPD, 0x3F);
    }
    else {
        rf_writeRegisterSimple(FEATURE, feature & ~0x04);
        rf_writeRegisterSimple(DYNPD, 0x00);
    }
}


void rf_enableACKPayloads(const bool enable) {
    rf_enableDynamicPayloads(enable);
    rf_writeRegisterSimple(FEATURE, (enable ? 0x06 : 0x00));
}


//TODO: implement this
uint8_t rf_handleIRQ() {
    uint8_t status;

    status = 0x00;

    return status;
}


uint8_t rf_readRXPayloadWidth() {
    uint8_t width;

    clearSPI();

    spi_ss(false);
    spi_write8_no_ss(R_RX_PL_WID);
    width = spi_write8_no_ss(NOP);
    spi_ss(true);

    if(width > 32) {
        rf_flushRX();
        width = 255;
    }
    
    return width;
}

// untested!
void rf_setStaticPayloadWidth(const uint8_t num, const uint8_t width){
    if(num < 5) {
        payloadSizes[num] = width;
        rf_writeRegisterSimple(rx_pipe_payloads[num], width);
    }
    else {
        uint8_t i = 6;
        payloadSize = width;
        while(i--) {
            rf_writeRegisterSimple(rx_pipe_payloads[num], width);
            payloadSizes[num] = width;
        }
    }
}


uint8_t rf_writeACKPayload(const uint8_t pipe, const uint8_t* buf, uint8_t len) {
    uint8_t status;

    status = spi_write(W_ACK_PAYLOAD | (pipe & 0x07), buf, len);

    return status;
}


uint8_t rf_readStatus() {
    clearSPI();

    return spi_write8(NOP);
}


void rf_setPALevel(const RF24_PWR_LVL pwr) {
    uint8_t rf_setup_reg;

    rf_readRegister(RF_SETUP, &rf_setup_reg, 1);
    rf_setup_reg &= 0xF9; // unset bits 1 and 2

    switch(pwr) { // low power is already set by unsetting bits 1 and 2
        case RF24_MINUS12_DBM:
            rf_setup_reg |= 0x02; // set bit 1
            break;
        case RF24_MINUS6_DBM:
            rf_setup_reg |= 0x04; // set bit 2
            break;
        case RF24_0_DBM:
            rf_setup_reg |= 0x06; // set bits 1 and 2
            break;
        case RF24_MINUS18_DBM:
            break;
    }
    
    rf_writeRegister(RF_SETUP, &rf_setup_reg, 1);
}


void rf_setDataRate(const RF24_SPEED speed) {
    uint8_t rf_setup_reg;
    
    rf_readRegister(RF_SETUP, &rf_setup_reg, 1);
    rf_setup_reg &= 0xD7;   // unset bits 3 and 5

    switch(speed) { // medium speed is already set by unsetting bits 3 and 5
        case RF24_250KBPS:
            rf_setup_reg |= 0x20; // set bit 5
            break;
        case RF24_2MBPS:
            rf_setup_reg |= 0x08; // set bit 3
            break;
        case RF24_1MBPS:
            break;
    }

    rf_writeRegister(RF_SETUP, &rf_setup_reg, 1);
}


void rf_setChannel(const uint8_t channel) {
    rf_writeRegister(RF_CH, &channel, 1);
}


void rf_setCRCMode(const RF24_CRC_MODE mode) {
    uint8_t config;

    rf_readRegister(CONFIG, &config, 1);

    if(mode == RF24_CRC_ONE_BYTE) {
        config |= 0x08;     // set EN_CRC bit
        config &= ~(0x04);  // clear CRCO bit
    }
    else if(mode == RF24_CRC_TWO_BYTES){
        config |= 0x0C;     // set EN_CRC and CRCO bits
    }
    else { // disable CRC
        // must force disable EN_AA to disable CRC
        rf_writeRegisterSimple(EN_AA, 0x00);
        
        config &= ~(0x0C);  // clear EN_CRC and CRCO bits
    }

    rf_writeRegister(CONFIG, &config, 1);
}


void rf_setNoACK(const bool noack) {
    uint8_t feature;

    noACKEnabled = noack;
    rf_readRegister(FEATURE, &feature, 1);
    rf_writeRegisterSimple(FEATURE, (noack ? (feature | 0x01) : (feature & ~0x01)));
}


void rf_setRetries(const uint8_t delay, const uint8_t count) {
    rf_writeRegisterSimple(SETUP_RETR, (delay<<4) | count);
}


void rf_setAddressWidth(const RF24_AW width) {
    if(width == RF24_AW_3BYTES) {
        rf_writeRegisterSimple(SETUP_AW, 0x01);
    }
    else if(width == RF24_AW_4BYTES) {
        rf_writeRegisterSimple(SETUP_AW, 0x02);
    }
    else { // 5 bytes default
        rf_writeRegisterSimple(SETUP_AW, 0x03);
    }
}


bool rf_testRPD() {
    uint8_t rpd;
    rf_readRegister(RPD, &rpd, 1);
    return (rpd & 1);
}


void rf_sendCarrier(const bool enable) {
    uint8_t config;
    uint8_t rf_setup;

    rf_readRegister(CONFIG, &config, 1);
    rf_readRegister(RF_SETUP, &rf_setup, 1);

    if(enable) {

        rf_writeRegisterSimple(CONFIG, 0x0E);
        rf_writeRegisterSimple(CONFIG, 0x96);

        rf_writeRegisterSimple(CONFIG, (config & 0xFC) | 0x02);
        rf_writeRegisterSimple(RF_SETUP, (rf_setup & 0x6F) | 0x90);
        rf_CE(true);
    }
    else {
        rf_CE(false);
        rf_writeRegisterSimple(RF_SETUP, (rf_setup & 0x6F));
        rf_writeRegisterSimple(CONFIG, (config & 0xFC));
    }
}


// this could be done in a loop because the register addresses increment by 1,
// but it's for debugging so whatever
void rf_debug(debug_registers* reg) {
    rf_readRegister(CONFIG, &reg->config, 1);
    rf_readRegister(EN_AA, &reg->en_aa, 1);
    rf_readRegister(EN_RXADDR, &reg->en_rxaddr, 1);
    rf_readRegister(SETUP_AW, &reg->setup_aw, 1);
    rf_readRegister(SETUP_RETR, &reg->setup_retr, 1);
    rf_readRegister(RF_CH, &reg->rf_ch, 1);
    rf_readRegister(RF_SETUP, &reg->rf_setup, 1);
    rf_readRegister(STATUS, &reg->status, 1);
    rf_readRegister(OBSERVE_TX, &reg->observe_tx, 1);
    rf_readRegister(RPD, &reg->rpd, 1);
    rf_readRegister(RX_ADDR_P0, (uint8_t*)&reg->rx_addr_p0, 5);
    rf_readRegister(RX_ADDR_P1, (uint8_t*)&reg->rx_addr_p1, 5);
    rf_readRegister(RX_ADDR_P2, &reg->rx_addr_p2, 1);
    rf_readRegister(RX_ADDR_P3, &reg->rx_addr_p3, 1);
    rf_readRegister(RX_ADDR_P4, &reg->rx_addr_p4, 1);
    rf_readRegister(RX_ADDR_P5, &reg->rx_addr_p5, 1);
    rf_readRegister(TX_ADDR, (uint8_t*)&reg->tx_addr, 5);
    rf_readRegister(RX_PW_P0, &reg->rx_pw_p0, 1);
    rf_readRegister(RX_PW_P1, &reg->rx_pw_p1, 1);
    rf_readRegister(RX_PW_P2, &reg->rx_pw_p2, 1);
    rf_readRegister(RX_PW_P3, &reg->rx_pw_p3, 1);
    rf_readRegister(RX_PW_P4, &reg->rx_pw_p4, 1);
    rf_readRegister(RX_PW_P5, &reg->rx_pw_p5, 1);
    rf_readRegister(FIFO_STATUS, &reg->fifo_status, 1);
    rf_readRegister(DYNPD, &reg->dynpd, 1);
    rf_readRegister(FEATURE, &reg->feature, 1);
}



/* ------ INTERNAL LIBRARY FUNCTIONS ------ */

/**
 * sets the PWR_UP bit in the config register to power up the rx / tx portions
 * of the chip.
 */
static void rf_powerUp() {
    uint8_t config;

    rf_readRegister(CONFIG, &config, 1);
    config |= 0x02;
    rf_writeRegister(CONFIG, &config, 1);
}

/**
 * clears the PWR_UP bit in the config register to power down the rx/tx
 * portions of the chip.
 */
static void rf_powerDown() {
    uint8_t config;

    rf_readRegister(CONFIG, &config, 1);
    config &= ~(0x02);
    rf_writeRegister(CONFIG, &config, 1);
}


/**
 * Internal function to turn on / off the CE (chip enable) pin
 *
 * @param enable false for disable, true for enable
 */
static void rf_CE(const bool enable) {
    if(enable) {
        *LAT_REG_CE |= (1<<CE_pin);
    }
    else {
        *LAT_REG_CE &= (0xFFFF-(1<<CE_pin));
    }
}

/**
 * Possibly unnecessary function that clears the PIC's SPI buffer and interrupt flag
 */
static void clearSPI() {
    uint16_t dummy;
    if(SPI1STATbits.SPIRBF){
        dummy = SPI1BUF;
    }
    if(SPI1STATbits.SPITBF) {
        spi_write8(0xFF);
    }
    if(IFS0bits.SPI1IF) {
        IFS0bits.SPI1IF = 0;
    }
}


/**
 * Internal function to read from the RF24 module's internal registers
 *
 * @param regMapAddr The hex address of the register.  (use mnemonics in rf24.h)
 * @param buf Buffer to receive the register value
 * @param len Number of bytes to read
 * @return STATUS register contents
 */
static uint8_t rf_readRegister(const uint8_t regMapAddr, uint8_t* buf, uint8_t len) {
    uint8_t status;

    clearSPI();

    status = spi_read(R_REGISTER | (REG_MASK & regMapAddr), NOP, buf, len);

    return status;
}

/**
 * Internal function to write to the RF24 module's internal registers
 *
 * @param regMapAddr Address of the register to write to (use mnemonics in rf24.h)
 * @param buf Buffer containing the data to write
 * @param len Number of bytes to write
 * @return STATUS register contents
 */
static uint8_t rf_writeRegister(const uint8_t regMapAddr, const uint8_t *buf, uint8_t len) {
    uint8_t status;
   // uint16_t dummy; // spi buffer data is put here to clear the SPIRBF flag

    clearSPI();

    status = spi_write(W_REGISTER | (REG_MASK & regMapAddr), buf, len);

    return status;
}

static uint8_t rf_writeRegisterSimple(const uint8_t regMapAddr, const uint8_t value) {
    uint8_t tmp = value;
    return rf_writeRegister(regMapAddr, &tmp, 1);
}

/**
 * Internal function to read a payload from the RX buffer
 *
 * @param buf Location to store the incoming data
 * @param len Number of bytes to read
 * @return STATUS register contents
 */
static uint8_t rf_readPayload(uint8_t *buf, uint8_t len) {
    uint8_t status;

    clearSPI();

    uint8_t data_size = (len < payloadSize ? len : payloadSize);
    uint8_t blank_size = (dynamicPayloadsEnabled ? 0 : payloadSize - len);

    spi_ss(false);
    status = spi_read_no_ss(R_RX_PAYLOAD, NOP, buf, data_size);
    while(blank_size--) {
        spi_write8_no_ss(0);
    }
    spi_ss(true);

    return status;
}

/**
 * Internal function to write a payload to the TX buffer.
 *
 * @param buf Buffer containing the data to write
 * @param len Number of bytes to write
 * @return STATUS register contents
 */
static uint8_t rf_writePayload(const uint8_t *buf, uint8_t len) {
    uint8_t status;
    //uint16_t dummy; // spi buffer data is put here to clear the SPIRBF flag

    clearSPI();

    uint8_t data_size = (len < payloadSize ? len : payloadSize);
    uint8_t blank_size = (dynamicPayloadsEnabled ? 0 : payloadSize - len);

    spi_ss(false);
    //status = spi_write8_no_ss(W_TX_PAYLOAD);
    status = spi_write8_no_ss(noACKEnabled ? W_TX_PAYLOAD_NOACK : W_TX_PAYLOAD);
    spi_write_no_ss(buf, data_size);
    while(blank_size--) {
        spi_write8_no_ss(0);
    }
    spi_ss(true);

    return status;
}
